package com.example

import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm
import com.example.entity.User
import com.fasterxml.jackson.databind.SerializationFeature
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.auth.jwt.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.http.cio.websocket.*
import io.ktor.jackson.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.websocket.*
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.util.*

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

@Suppress("unused") // Referenced in application.conf
@kotlin.jvm.JvmOverloads
fun Application.module(testing: Boolean = false) {
    //跨域
    install(CORS) {
        method(HttpMethod.Options)
        method(HttpMethod.Get)
        method(HttpMethod.Post)
        method(HttpMethod.Put)
        method(HttpMethod.Delete)
        method(HttpMethod.Patch)
        header(HttpHeaders.Authorization)
        allowCredentials = true
        anyHost()
    }


    install(WebSockets) {
    }
    //支持大容量消息
    install(PartialContent) {
    }
    //定义json处理
    install(ContentNegotiation) {
        jackson {
            enable(SerializationFeature.INDENT_OUTPUT)
        }
    }
    //全局异常处理
    install(StatusPages) {
        exception<InvalidCredentialsException> { exception ->
            call.respond(HttpStatusCode.Unauthorized, mapOf("OK" to false, "error" to (exception.message ?: "")))
        }
    }
    //定义Authentication
    val simpleJwt = SimpleJWT("key")
    install(Authentication) {
        jwt {
            verifier(simpleJwt.verifier)
            validate {
                UserIdPrincipal(it.payload.audience.first())
            }
        }
    }

    routing {
        post("/login") {
            val post = call.receive<User>()
            post.id = 1
            if (false)
                throw InvalidCredentialsException("Invalid credentials")
            call.respond(mapOf("token" to simpleJwt.sign(post)))
        }

        authenticate {
            val chatClients = Collections.synchronizedSet(LinkedHashSet<ChatClient>())
            webSocket("/chat") { // this: DefaultWebSocketSession
                val principal = call.principal<UserIdPrincipal>()
                val chatClient = principal?.let { ChatClient(this, it) }
                chatClients+=chatClient
                try {
                    while (true) {
                        when (val frame = incoming.receive()) { // suspend
                            is Frame.Text -> {
                                val text = frame.readText()
                                outgoing.send(Frame.Text(text)) // suspend
                            }
                        }
                    }
                } finally {
                    chatClients-=chatClient
                }

            }
            get("/") {
                val principal = call.principal<UserIdPrincipal>() ?: error("No principal")
                call.respond(mapOf("OK" to true))
            }

        }
    }


}


class SimpleJWT(secret: String) {
    private val algorithm = Algorithm.HMAC256(secret)
    val verifier: JWTVerifier = JWT.require(algorithm).build()
    fun sign(user: User): String {
        return JWT.create().withAudience(user.id.toString())
            .withIssuedAt(Date.from(LocalDateTime.now().toInstant(ZoneOffset.ofHours(8))))
            .withExpiresAt(Date.from(LocalDateTime.now().plusMonths(1).toInstant(ZoneOffset.ofHours(8))))
            .sign(algorithm)
    }
}


class InvalidCredentialsException(message: String) : RuntimeException(message)


class ChatClient(val session: DefaultWebSocketSession, val userId: UserIdPrincipal) {

}